package langnstats.project.tools;


import java.util.List;

public class StatisticsToolkit {
	
	public static class YeriDigitOverflowException extends RuntimeException{
		public static final long serialVersionUID = 1;
		
		public YeriDigitOverflowException(){ super(); }
		public YeriDigitOverflowException(String message){ super(message); }
		public YeriDigitOverflowException(String message, Throwable cause){super(cause);}
		public YeriDigitOverflowException(Throwable cause){ super(cause); }
		
		public YeriDigitOverflowException(int number, int digit){ 
			this("Number: "+number+", Digit: "+digit); }
	}
	
	
	public static String makeDigitedNumberString(int number, int digit)
	throws YeriDigitOverflowException{
		StringBuffer returnString = new StringBuffer();
		int current;
		
		if(number<0) throw new YeriDigitOverflowException(number, digit);
		
		for(int i=0; i<digit; i++){
			current = number % 10;
			returnString.insert(0, current);
			number /= 10;
		}
//		if(number>0) throw new YeriDigitOverflowException(number, digit);
		return returnString.toString();
	}
	
	public static int findGCF(int a, int b){
		// returns big when small=0
		int absA = Math.abs(a);
		int absB = Math.abs(b);
		
		int big = absA>=absB?absA:absB;
		int small = absB<=absA?absB:absA;
		int r=small;
		if(small<=0){ throw new IllegalArgumentException("All arguments must be >0. Not "+small); }
		while(r>0){
			r=big%small;
			big=small;
			small=r;			
		}
		return big;
	}
	
	public static int pow(int a, int n){
		if(n<0){ throw new IllegalArgumentException("Power n must be >=0. Not "+n); }
		int answer = 1;
		for(int i=0; i<n; i++){ answer *= a; }
		return answer;
	}
	public static double average(int[] numbers){
		int sum = 0;
		for(int number : numbers){ sum += number; }
		return ((double)sum)/numbers.length;
	}
	public static double average(double[] ds){
		double result = 0;
		for(double d:ds){ result += d; }
		return result/ds.length;
	}
	public static double variance(double[] ds){
		double result = 0;
		for(double d:ds){ result += (d*d); }
		result /= ds.length;
		return result-average(ds);
	}
	public static double stddev(double[] ds){ return Math.sqrt(variance(ds)); }
	public static void main(String[] args){
		double[] ds = {
				0.968965517241379,
				0.975862068965517,
				0.972413793103448,
				0.989655172413793,
				0.993127147766323,
				
		}; 
		System.out.println(stddev(ds));		
	}
	
	public static double log(double base, double a){ return Math.log(a)/Math.log(base); }
	
	public static double entropy(List<? extends Object> oArray){
		CountMap<Object> countMap = new CountMap<Object>();
		countMap.increCountAll(oArray);
		return countMap.entropy();
	}
	
	public static double entropy(Object[] oArray){
		CountMap<Object> countMap = new CountMap<Object>();
		countMap.increCountAll(oArray);
		return countMap.entropy();
	}
	
	public static double entropy(int[] counts){
		int sum = 0;
		for(int count: counts){ sum += count; }
		
		double[] probabilities = new double[counts.length];
		for(int i=0; i<counts.length; i++){ probabilities[i] = ((double)counts[i]) / sum; }
		
		return entropy(probabilities);
	}
	
	public static double entropy(double[] probabilities){
		return entropy(probabilities, 0.00001);
	}
	public static double entropy(double[] probabilities, double threshold)
	throws RuntimeException{
		double total = 0;
		for(double p : probabilities){ total += p; }
		if(Math.abs(total-1)>threshold){ throw new RuntimeException("Probabilities doesn't sum up to 1."); }
		
		double entropy = 0;
		for(double p : probabilities){ entropy += -p * log(2,p); }
		return entropy;
	}
	
	public static double avgLogLikelihood(double[] likelihoods){
		double returnValue = 0;
		for(double p : likelihoods){ returnValue += log(2,p); }
		return returnValue / likelihoods.length;
	}
}
